package org.wikinews.en.wikinewsreader;

import java.util.ArrayList;
import java.util.HashMap;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings.Secure;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;


public class WikinewsReader extends ListActivity{
   	private static final String TAG = "WikinewsReader" ;
    private static Context ctx;
    private static Boolean errTrigger;
    private static String baseURL = "http://en.wikinews.org/wiki/";
    
	// Basis for Custom ListView and adapter from 
	// http://www.softwarepassion.com/android-series-custom-listview-items-and-adapters/
    private ProgressDialog progDialog = null;
    public ArrayList<News> allNews = new ArrayList<News>();
    private NewsAdapter newsAdapter;
    private Runnable viewNews;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
    	setContentView(R.layout.main);
    	Log.d(TAG, "WikinewsReader.onCreate()");
    	
    	// Context to be abused in statics at the bottom
    	ctx = this;
    	setErrorTrigger(false); // Must reset trigger here, else on "finish" and return, it wont 
    							// show any err messages because the variable is still set to true.

    } // End onCreate()
    
    @Override
    public void onResume() {
    	 super.onResume();
    	 Log.d(TAG,"onResume()");

    	 this.newsAdapter = new NewsAdapter(this, R.layout.row, allNews);
         setListAdapter(this.newsAdapter);

         Log.d(TAG, "onResume.allnews.Size()="+allNews.size());
         Log.d(TAG, "onResume.newsAdapter.getCount()="+this.newsAdapter.getCount());

         if(allNews.size() != 0){
        	 Log.d(TAG, "Data already here");
         	//Data has been restored (likely from resuming)
         	//We don't need to do anything.
         }else{
        	progDialog = ProgressDialog.show(WikinewsReader.this, "Please wait...", "Getting latest news", true);
         	Log.d(TAG, "Need to get data");
 	        //Run the getNews() bit in it's own thread
 	        viewNews = new Runnable(){
 	            @Override
 	            public void run() {
 	                getNews();
 	            }
 	        };
 	        Thread thread =  new Thread(null, viewNews, "MagentoBackground");
 	        thread.start();
         }

    }
    
    @Override
    public void onPause() {
    	 super.onPause();
    	 Log.d(TAG,"onPause()");
         Log.d(TAG, "onPause.allnews.Size()="+allNews.size());
         Log.d(TAG, "onPause.newsAdapter.getCount()="+this.newsAdapter.getCount());

    }

    
    // Example of InstanceState Save/Restore from
    // http://tseng-blog.nge-web.net/blog/2009/02/27/savedstate-preserve-data-when-application-is-recreated/
    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
    	Log.d(TAG,"onRestoreInstanceState()");
    	
    	allNews = savedInstanceState.getParcelableArrayList("allNews");
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
    	Log.d(TAG,"onSaveInstanceState()");
    	outState.putParcelableArrayList("allNews", allNews);
    }
    
    
    /*
     * Puts the menus up on screen based on the XML(non-Javadoc)
     * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
	    super.onCreateOptionsMenu(menu);
	    MenuInflater inflater = getMenuInflater();
	    inflater.inflate(R.menu.menu, menu);
	    return true;
    }

    /*
     * Make the menu options do something(non-Javadoc)
     * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
	    switch (item.getItemId()) {
		    case R.id.settings: 
			    startActivity(new Intent(this, Prefs.class));
			    return true;
		    case R.id.exit:
		    	finish();
		    	return true;
		    case R.id.submitbug:
		    	startActivity(new Intent(this, SubmitBug.class));
			    return true;
		    case R.id.about:
		    	startActivity(new Intent(this, About.class));
			    return true;
		    case R.id.launchweb:
		        Uri uri = Uri.parse(getBaseURL());
		        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
		        startActivity(intent);
	    }
	    return false;
    }
    
    /*
     * Watches for changes in the ArrayList, adds them to the screen, calls refresh
     */
    private Runnable returnRes = new Runnable() {
        @Override
        public void run() {
            if(allNews != null && allNews.size() > 0){
                newsAdapter.notifyDataSetChanged();
                for(int i=0;i<allNews.size();i++){
                	Log.d(TAG, "newsAdapter.add(allNews.get(i)");
                	newsAdapter.add(allNews.get(i));
                }
                
        	}
            // If the dialog isn't up (Because there was already data)
            // we don't want to try and dismiss it.
            if(progDialog != null){ 
            	progDialog.dismiss(); // There is data, no more need for progress dialog
            }
            newsAdapter.notifyDataSetChanged(); // Calls refresh
        }
    }; // End Runnable
    
    /*
     * Overly complicated manner of extracting data from webserver,
     * reproccessing the provided JSON, sorting the JSON,
     * stuffing the data into a multi-dimensional array,
     * then finally writing into the ArrayList (in order)
     */
    private void getNews(){
          try{
        	  	Log.d(TAG, "WikinewsReader.getNews()");
				allNews = new ArrayList<News>();
				String[][] sending = {{"mode",""}};
				if(Prefs.getDownloadondemand(getBaseContext())){
					//If dod = true, get "latest" which is only headers
					sending[0][1] = "latest";
				}else{
					//If dod = false (default), get entire articles
					sending[0][1] = "latestfull";
				}
				String res = NetTrick.doIt(sending, handler);
				
				if(res == null){
					generalError(13, ctx);
				}else{
					//Declaring the JSON Object out here in case I need it again.
					JSONObject jj = null;
					
					//Get the main level of JSON objects
					Log.d(TAG, "Main JSON");
					jj = new JSONObject(res);
					JSONArray nameArray = jj.names();
					JSONArray valArray = jj.toJSONArray(nameArray);
					
					Log.d(TAG,valArray.toString());
					Log.d(TAG, "Hashmap");
					//Start the hashmap that we're going to use for column header to array id
					HashMap<String,Integer> myMap = new HashMap<String,Integer>();
					
					Log.d(TAG, "Child JSON 1");
					//We need this one time JSON child array to get the length for the big multidem array
					JSONObject j2 = new JSONObject(valArray.getString(0));  //TODO: change format from object directly to array?
					JSONArray xName = j2.names();
					JSONArray xVal = j2.toJSONArray(xName);
					
					Log.d(TAG,xVal.toString());
					
					Log.d(TAG, "Create Array");
					//Setup the big multi dem array
					String[][] allData = new String[valArray.length()][xVal.length()];
					Log.d(TAG, "Starting JSON->MultiDem Array["+valArray.length()+"]["+xVal.length()+"]");
					
					//Loading multi dem array
					for(int i=0; i<valArray.length(); i++){
	//					Log.d(TAG,"Interation #"+i);
						JSONObject j3 = new JSONObject(valArray.getString(i));
						JSONArray subName = j3.names();
						JSONArray subVal = j3.toJSONArray(subName);
						
						//If the column header->array map is empty, populate it. Assume all iterations are same map
						if(myMap.isEmpty()){
							Log.d(TAG,"Creating MyMap");
						for(int z=0; z<subName.length(); z++){
	//						Log.d(TAG,"put " + subName.getString(z) + " value " + z);
								myMap.put(subName.getString(z), z);
							}
						}
						
						//Actually put data into big multi dem array. First level is out of order, second is specific order.
	//					Log.d(TAG,"allData["+(nameArray.getInt(i)-1)+"]["+myMap.get("id")+"] = "+subVal.getString(myMap.get("id")));
						allData[nameArray.getInt(i)-1][0] = subVal.getString(myMap.get("id"));
						allData[nameArray.getInt(i)-1][1] = subVal.getString(myMap.get("title"));
						allData[nameArray.getInt(i)-1][2] = subVal.getString(myMap.get("title"));
						allData[nameArray.getInt(i)-1][3] = subVal.getString(myMap.get("revid"));
						allData[nameArray.getInt(i)-1][4] = subVal.getString(myMap.get("last_update"));
						if(!Prefs.getDownloadondemand(getBaseContext())){
							//If we're downloading everything, store the full text of the article too.
							allData[nameArray.getInt(i)-1][5] = subVal.getString(myMap.get("fulltext"));
						}
					}
					
					//Reads big multidem array (in proper order), append data into string for web print.
					Log.d(TAG,"Done store, playing back");
					for(int i=0; i<allData.length; i++){
	//					Log.d(TAG,"Storing Article " + allData[i][1]);
	
						//Store the data, now in order into the ArrayList from the multi-dem array
						News nTemp = new News(null);
						nTemp.setNewsID(Integer.parseInt(allData[i][0]));
						nTemp.setNewsTitle(allData[i][1]);
						nTemp.setNewsURL(allData[i][2]);
						nTemp.setNewsRevid(Integer.parseInt(allData[i][3]));
						nTemp.setNewsLastUpdate(allData[i][4]);
						if(!Prefs.getDownloadondemand(getBaseContext())){
							//If we downloaded everything, we have the full text to set too
							nTemp.setNewsFulltext(allData[i][5]);
						}
						allNews.add(nTemp);
					}
					    					
					Log.d(TAG, "Storage Count "+ allNews.size());
				}
          	} catch (JSONException e) {
          		// TODO Auto-generated catch block
          		Log.e(TAG, e.getStackTrace().toString());
          		handler.sendEmptyMessage(12);
          	} catch (Exception e) {
            	Log.e(TAG, e.getMessage().toString());
            	handler.sendEmptyMessage(11);
            }
            runOnUiThread(returnRes);
        }
    
    /*
     * Creates a row on the ListView for each call (each news item)
     * UI layout template from /res/layout/row.xml
     * then populated the text fields with the proper news data
     * lastly creates click listeners for each unique line.
     */
    private class NewsAdapter extends ArrayAdapter<News> {

        private ArrayList<News> items;

        public NewsAdapter(Context context, int textViewResourceId, ArrayList<News> items) {
                super(context, textViewResourceId, items);
                this.items = items;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
                View v = convertView;
                if (v == null) {
                    LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    v = vi.inflate(R.layout.row, null);
                }
                final News n = items.get(position);
                if (n != null) {
                        TextView tt = (TextView) v.findViewById(R.id.toptext);
                        TextView bt = (TextView) v.findViewById(R.id.bottomtext);
                        if (tt != null) {
                              tt.setText(n.getNewsTitle());
                        }
                        if(bt != null){
                              bt.setText(getResources().getString(R.string.last_update) +": "+ n.getNewsLastUpdate());
                        }
                        
                /*
                 * Click listener created for each news item, 
                 * on click, sends the user over to Activity w/ the
                 * selected stories data. n.getNewsFulltext might be null
                 */
                v.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
        				Log.d(TAG, "Clicked on ID " + n.getNewsID().toString());
            			Intent intent = new Intent(WikinewsReader.this, Article.class);
            			String[] shortRay = {n.getNewsTitle(), n.getNewsURL(), n.getNewsFulltext()};
            			intent.putExtra("id", n.getNewsID());
            			intent.putExtra("all", shortRay);
            			startActivity(intent);
                    }
                });

                }
                return v;
        }
    }
    
    public static int getVersionCode(){
        int version = 1;
        try {
             PackageInfo pInfo = ctx.getPackageManager().getPackageInfo("org.wikinews.en.wikinewsreader", PackageManager.GET_META_DATA);
             version = pInfo.versionCode;
        } catch (NameNotFoundException e) {     }
        return version;
   }

    public static String getVersionName(){
        String version = "null";
        try {
             PackageInfo pInfo = ctx.getPackageManager().getPackageInfo("org.wikinews.en.wikinewsreader", PackageManager.GET_META_DATA);
             version = pInfo.versionName;
        } catch (NameNotFoundException e) {     }
        return version;
   }
    
    public static String getAndroidID(){
    	Log.d(TAG,"getAndroidID()");    
    	String androidID = Secure.getString(ctx.getContentResolver(),Secure.ANDROID_ID);	
    	return androidID;
    }

    private Handler handler = new Handler() {
		public void handleMessage(Message msg){
    		Log.d(TAG, "WikinewsReader.handler.messageHandler("+msg.toString()+")");
    		generalError(msg.what, ctx);
    	}
    };

    public static void generalError(Integer id, final Context context){
    	Log.d(TAG, "WikinewsReader.generalError("+id.toString()+", "+context.toString()+")");
    	if(!getErrorTrigger()){
	    	Builder builder = new AlertDialog.Builder(context);
	        builder.setTitle("An Error has occured");  
	        String msg = null;
	        if(id == 11){
	    		Log.e(TAG,"General Exception");
	            msg = "General Exception";
	    	}else if(id == 12){
	    		Log.e(TAG,"JSON Exception");
	    		msg = "JSON Exception";
	    	}else if(id == 13){
	    		Log.e(TAG,"Null Error");
	    		msg = "Null Error";
	    	}else if(id == 14){
	    		Log.e(TAG,"ClientProtocol Exception");
	    		msg = "ClientProtocol Exception";
	    	}else if(id == 15){
	    		Log.e(TAG,"UknownHost Exception");
	    		msg = "UknownHost Exception";
	    	}else if(id == 16){
	    		Log.e(TAG,"IO Exception");
	    		msg = "IO Exception";
	    	}
	
	        builder.setMessage(msg);
	        Log.d(TAG, "Launching Builder");
	        builder.setPositiveButton("Close", new DialogInterface.OnClickListener() {
	            public void onClick(DialogInterface dialog, int id) {
	            	Log.d(TAG, "Calling finish()");
	                ((Activity) context).finish();
	                //TODO: Make this exit the application versus just closing whatever window.
	           }
	        });
	        builder.show();
	        setErrorTrigger(true);
    	}
    }
    
    // ErrorTrigger used specifically to prevent double 
    // warning popups & crash on leakly window.
    public static Boolean getErrorTrigger(){
    	return errTrigger;
	}
	public static void setErrorTrigger(Boolean s){
		errTrigger = s;
	}
	public static String getBaseURL(){
		return baseURL;
	}

}